#include "cpu/exec.h"

make_EHelper(add) {
	// TODO();
	rtl_sext(&t1, &id_dest->val, id_dest->width);
	rtl_sext(&t2, &id_src->val, id_src->width);
	// t1 + t2
	// OF针对无符号 CF针对有符号
	rtl_add(&t0, &t1, &t2);
	t3 = (t0 < t1);
	rtl_set_CF(&t3);
	t3 = ((((int32_t)(t1) >= 0) ^ (((int32_t)(t2) >= 0 ))) && (((int32_t)(t0) < 0) ^ (((int32_t)(t2) >= 0 )) )); //正正得负 负负得正
	rtl_set_OF(&t3);
	rtl_update_ZFSF(&t0, 4);
	operand_write(id_dest, &t0);

	print_asm_template2(add);
}

make_EHelper(sub) {
	// TODO();
	rtl_sext(&t1, &id_dest->val, id_dest->width);
	rtl_sext(&t2, &id_src->val, id_src->width);

	rtl_sub(&t0, &t1, &t2);
	t3 = (t0 > t1);
	rtl_set_CF(&t3);
	t3 = ((((int32_t)(t1) < 0) == (((int32_t)(t2) >> 31) == 0)) && (((int32_t)(t0) < 0) != ((int32_t)(t1) < 0))); // 负正得正 正负得负
	rtl_set_OF(&t3);
	rtl_update_ZFSF(&t0, 4);
	operand_write(id_dest, &t0);

	print_asm_template2(sub);
}

make_EHelper(cmp) {
	// TODO();
	rtl_sext(&t1, &id_dest->val, id_dest->width);
	rtl_sext(&t2, &id_src->val, id_src->width);

	rtl_sub(&t0, &t1, &t2);
	t3 = (t0 > t1);
	rtl_set_CF(&t3);
	t3 = ((((int32_t)(t1) < 0) == (((int32_t)(t2) >> 31) == 0)) && (((int32_t)(t0) < 0) != ((int32_t)(t1) < 0)));
	rtl_set_OF(&t3);
	rtl_update_ZFSF(&t0, 4);

	print_asm_template2(cmp);
}

make_EHelper(inc) {
	// TODO();
	//printf("inc %d\n", *id_dest);
	rtl_addi(&t2, &id_dest->val, 1);
	operand_write(id_dest, &t2);
	rtl_update_ZFSF(&t2, id_dest->width);
	rtl_xor(&t0, &id_dest->val, &id_src->val);
	rtl_not(&t0, &t0);
	rtl_xor(&t1, &id_dest->val, &t2);
	rtl_and(&t0, &t0, &t1);
	rtl_msb(&t0, &t0, id_dest->width);
	rtl_set_OF(&t0);
	//printf("inc2 %d\n", id_dest);
	print_asm_template1(inc);
}

make_EHelper(dec) {
	// TODO();
	//printf("dec 1\n");
	rtl_subi(&t2, &id_dest->val, 1);
	operand_write(id_dest, &t2);

	rtl_update_ZFSF(&t2, id_dest->width);

	rtl_xor(&t0, &id_dest->val, &id_src->val);
	rtl_xor(&t1, &id_dest->val, &t2);
	rtl_and(&t0, &t0, &t1);
	rtl_msb(&t0, &t0, id_dest->width);
	rtl_set_OF(&t0);
	//printf("dec 2\n");
	print_asm_template1(dec);
}

make_EHelper(neg) {
	// TODO();
	rtl_mv(&t0, &id_dest->val);
	rtl_not(&t0, &t0);
	rtl_addi(&t0, &t0, 1);
	operand_write(id_dest, &t0);

	t1 = (id_dest->val != 0);
	rtl_set_CF(&t1);

	rtl_update_ZFSF(&t0, id_dest->width);
	rtl_xor(&t1, &t0, &id_dest->val);
	rtl_not(&t1, &t1);
	rtl_msb(&t1, &t1, id_dest->width);
	rtl_set_OF(&t1);

	print_asm_template1(neg);
}

make_EHelper(adc) {
	rtl_add(&t2, &id_dest->val, &id_src->val);
	rtl_setrelopi(RELOP_LTU, &t3, &t2, id_dest->val);
	rtl_get_CF(&t1);
	rtl_add(&t2, &t2, &t1);
	operand_write(id_dest, &t2);

	rtl_update_ZFSF(&t2, id_dest->width);

	rtl_setrelopi(RELOP_LTU, &t0, &t2, id_dest->val);
	rtl_or(&t0, &t3, &t0);
	rtl_set_CF(&t0);

	rtl_xor(&t0, &id_dest->val, &id_src->val);
	rtl_not(&t0, &t0);
	rtl_xor(&t1, &id_dest->val, &t2);
	rtl_and(&t0, &t0, &t1);
	rtl_msb(&t0, &t0, id_dest->width);
	rtl_set_OF(&t0);

	print_asm_template2(adc);
}

make_EHelper(sbb) {
	rtl_sub(&t2, &id_dest->val, &id_src->val);
	rtl_setrelopi(RELOP_LTU, &t3, &id_dest->val, t2);
	rtl_get_CF(&t1);
	rtl_sub(&t2, &t2, &t1);
	operand_write(id_dest, &t2);

	rtl_update_ZFSF(&t2, id_dest->width);

	rtl_setrelopi(RELOP_LTU, &t0, &id_dest->val, t2);
	rtl_or(&t0, &t3, &t0);
	rtl_set_CF(&t0);

	rtl_xor(&t0, &id_dest->val, &id_src->val);
	rtl_xor(&t1, &id_dest->val, &t2);
	rtl_and(&t0, &t0, &t1);
	rtl_msb(&t0, &t0, id_dest->width);
	rtl_set_OF(&t0);

	print_asm_template2(sbb);
}

make_EHelper(mul) {
	rtl_lr(&t0, R_EAX, id_dest->width);
	rtl_mul_lo(&t1, &id_dest->val, &t0);

	switch (id_dest->width) {
		case 1:
			rtl_sr(R_AX, &t1, 2);
			break;
		case 2:
			rtl_sr(R_AX, &t1, 2);
			rtl_shri(&t1, &t1, 16);
			rtl_sr(R_DX, &t1, 2);
		  break;
		case 4:
			rtl_mul_hi(&t2, &id_dest->val, &t0);
			rtl_sr(R_EDX, &t2, 4);
			rtl_sr(R_EAX, &t1, 4);
			break;
		default: assert(0);
	}

	print_asm_template1(mul);
}

// imul with one operand
make_EHelper(imul1) {
	rtl_lr(&t0, R_EAX, id_dest->width);
	rtl_imul_lo(&t1, &id_dest->val, &t0);

	switch (id_dest->width) {
		case 1:
			rtl_sr(R_AX, &t1, 2);
		break;
		case 2:
			rtl_sr(R_AX, &t1, 2);
			rtl_shri(&t1, &t1, 16);
			rtl_sr(R_DX, &t1, 2);
			break;
		case 4:
			rtl_imul_hi(&t2, &id_dest->val, &t0);
			rtl_sr(R_EDX, &t2, 4);
			rtl_sr(R_EAX, &t1, 4);
			break;
		default: assert(0);
	}

	print_asm_template1(imul);
}

// imul with two operands
make_EHelper(imul2) {
	rtl_sext(&t0, &id_src->val, id_src->width);
	rtl_sext(&t1, &id_dest->val, id_dest->width);

	rtl_imul_lo(&t2, &t1, &t0);
	operand_write(id_dest, &t2);

	print_asm_template2(imul);
}

// imul with three operands
make_EHelper(imul3) {
	rtl_sext(&t0, &id_src->val, id_src->width);
	rtl_sext(&t1, &id_src2->val, id_src->width);
	rtl_sext(&id_dest->val, &id_dest->val, id_dest->width);

	rtl_imul_lo(&t2, &t1, &t0);
	operand_write(id_dest, &t2);

	print_asm_template3(imul);
}

make_EHelper(div) {
	switch (id_dest->width) {
		case 1:
			rtl_lr(&t0, R_AX, 2);
			rtl_div_q(&t2, &t0, &id_dest->val);
			rtl_div_r(&t3, &t0, &id_dest->val);
			rtl_sr(R_AL, &t2, 1);
			rtl_sr(R_AH, &t3, 1);
			break;
		case 2:
			rtl_lr(&t0, R_AX, 2);
			rtl_lr(&t1, R_DX, 2);
			rtl_shli(&t1, &t1, 16);
			rtl_or(&t0, &t0, &t1);
			rtl_div_q(&t2, &t0, &id_dest->val);
			rtl_div_r(&t3, &t0, &id_dest->val);
			rtl_sr(R_AX, &t2, 2);
			rtl_sr(R_DX, &t3, 2);
			break;
		case 4:
			rtl_lr(&t0, R_EAX, 4);
			rtl_lr(&t1, R_EDX, 4);
			rtl_div64_q(&cpu.eax, &t1, &t0, &id_dest->val);
			rtl_div64_r(&cpu.edx, &t1, &t0, &id_dest->val);
			break;
		default: assert(0);
	}

	print_asm_template1(div);
}

make_EHelper(idiv) {
	switch (id_dest->width) {
		case 1:
			rtl_lr(&t0, R_AX, 2);
			rtl_idiv_q(&t2, &t0, &id_dest->val);
			rtl_idiv_r(&t3, &t0, &id_dest->val);
			rtl_sr(R_AL, &t2, 1);
			rtl_sr(R_AH, &t3, 1);
			break;
		case 2:
			rtl_lr(&t0, R_AX, 2);
			rtl_lr(&t1, R_DX, 2);
			rtl_shli(&t1, &t1, 16);
			rtl_or(&t0, &t0, &t1);
			rtl_idiv_q(&t2, &t0, &id_dest->val);
			rtl_idiv_r(&t3, &t0, &id_dest->val);
			rtl_sr(R_AX, &t2, 2);
			rtl_sr(R_DX, &t3, 2);
			break;
		case 4:
			rtl_lr(&t0, R_EAX, 4);
			rtl_lr(&t1, R_EDX, 4);
			rtl_idiv64_q(&cpu.eax, &t1, &t0, &id_dest->val);
			rtl_idiv64_r(&cpu.edx, &t1, &t0, &id_dest->val);
			break;
		default: assert(0);
	}

	print_asm_template1(idiv);
}
